﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Security.Policy;
using System.Text;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Soap;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using System.IO;

/*
 * 
 * написать программу которая выделяла подрядчиков типа
 * статитические методы это обычные функции и принадлежат типу
 * если задавать вопрос объекту "Что ты можешь"  выдаются Методы
 * если типу то статметоды
 * 
 * 
 * 
 * 
 * все методы можно представлять как FUNC<T1,T2,,,Tresult>
 * 
 * 
 */




namespace конфигуратор
{
    /// <summary>
    /// класс объектов типа паспорт
    /// </summary>

    //[Serializable]
    public class Паспорт   : INotifyPropertyChanged
    {
      
        private  Guid _ун;
        public string УН { get { return _ун.ToString(); } set { _ун = Guid.Parse(value); } }
        public Паспорт()
        {
            _ун = Guid.NewGuid();
        }

        //public Guid УНПредка { get; set; }

        // имена объекта относительны
        // еще их можно поделить по типам
        // - имя объекта (Шарик)
        // - имя группы к кот он относит (собака, 
        // группы могут (типами, функционал= ездовая, с точки зрения другого животного - еда (съедобный объект)
         private string _имя;
        public string Имя { get { return _имя; }  set {_имя = value;  Обновить(  ""  ); } }

        //public string ТекстПрограммы;      // в самом паспорте может быть код, а может и скорее всего здесь должен быть адрес кода, адрес кода должен
        // совпадать с адресом сборки в VS это правило не работает
        //public string АдресКода;            // сборка это тоже неисполнимый файл - это тоже код и из него можно получить текст в формате С# хотя и неточный
        
        public string АдресСпячки;//место хранения состояния объекта
        public bool СвойСохранительВосстановитель; // встроенный  или свой 
        public string ИмяМетодаСохранителя;
        public string ИмяМетодаВосстановителя;

         [XmlIgnore]
        public string Файл { get { return АдресСпячки + "\\" + Имя + ".объект"; } }        // имя+адрес может быть и в сети 
        // и памяти и в базе данных
        // адрес хранения должен быть типа uri ( имя_службы (file, tcp, http, SQL-server, ), адрес )
        // кроме того можно ввести несколько адресов : в сети , на диске, Объект это адрес в памяти = memory
        // диспетчер тоже служба объектов
        // адрес это команда которая ссотоит из субъекта и 

        public АдресФайла АдресФайла= new АдресФайла();

        private string _адресСборки;
        public string АдресСборки{ get { return _адресСборки; } set
        {
           if (_сборка!=null) return;
            _адресСборки = value;
            Обновить("");
        } } //

        public bool КэшироватьЗависимыеСборки;
        public string ПолноеИмяСборки { get; set; } //имя , культура, токен и т.д. не обязателен  если есть файл определяется автоматически
        private string _полноеИмяТипа;
        public string ПолноеИмяТипа { get
        {
            if (Тип!=null) return Тип.FullName;
            return _полноеИмяТипа;
        }
            set { if ( Тип == null ) _полноеИмяТипа = value; }
        } 

        private Assembly _сборка;

         [XmlIgnore]
        public Assembly Сборка {get { return _сборка; } set {
             
                if ( _объект == null && _тип == null )
                {
                     if (value==_сборка)    return;
                     if ( value != null )
                     {
                         this.ПолноеИмяСборки = value.FullName;
                         this.АдресСборки = value.CodeBase;
                     }
                     else ПолноеИмяСборки = АдресСборки = "";
                    _сборка = value;
                       Обновить( "" );
                  
                }
               
            }}    
        
        private Type _тип;

         [XmlIgnore]
        public Type Тип
        {
            get { return _тип; }
            set {
                if (  _объект == null)
                {
                    if ( value != null )
                    {
                          _тип = null;
                        Сборка = value.Assembly;
                        _тип = value;   
                        ПолноеИмяТипа = value.FullName;
                    }
                    else
                    {   
                        _тип = null;
                        ПолноеИмяТипа = "";
                        Обновить( "ПолноеИмяТипа" ); 
                        //Обновить( "Сборка" );
                         //Обновить( "ПолноеИмяСборки" ); Обновить( "АдресСборки" ); 
                    }
                    Обновить( "Тип" );    
                }
               
            }
        }

         private object _объект;

         [XmlIgnore]
        public object Объект    //адрес объекта в памяти
        {
            get { return _объект; }
            set
            {
                if ( value != null )
                {
                    _объект = null;
                    Тип = value.GetType();
                     _объект = value;
                     _ун = Guid.NewGuid();
                }
                else
                {
                    _объект = null;
                }
                Обновить( "Объект" );
            }
        }

        


        public override string ToString ()
        {
            if (Имя!=null)
            return base.ToString()+" Имя="+ Имя;
            return base.ToString();
        }
                                                                                                      
        public event PropertyChangedEventHandler PropertyChanged;
        public void Обновить ( string имяProperty )
        {
            if ( PropertyChanged != null )
                PropertyChanged( this, new PropertyChangedEventArgs(имяProperty) );
        }



         [XmlIgnore]
        public static Assembly[] СписокЗагруженныхСборок { get
        {
            var gg =    AppDomain.CurrentDomain.GetAssemblies();
            return gg;
        } 
        }
        public static Type[] ПросмотретьТипы( string адресСборки)
        {
            //var dom= AppDomain.CreateDomain("reflect");
            //AppDomainSetup appDomainSetup = dom.SetupInformation;
            //appDomainSetup.ApplicationBase = (new FileInfo(адресСборки)).Directory.ToString();
            ////appDomainSetup.ShadowCopyFiles = "true";
            ////appDomainSetup.CachePath = "cash";
            //var ass333= dom.GetAssemblies();

            //var ass2=   dom.Load(new AssemblyName(){ CodeBase = адресСборки} );
            //var tttt = ass2.GetTypes();
            //AppDomain.Unload(dom);
            ////return tttt;

            //Assembly aa=null;
            //var u2 = new Uri(адресСборки);
            //foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            //{
            //    Uri u = new Uri(assembly.CodeBase);
            //    if(u ==  u2 )
            //   {
            //       aa = assembly;
            //       break;
            //   }
            //}
            //if ( aa != null ) return aa.GetTypes();

            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += CurrentDomain_ReflectionOnlyAssemblyResolve;
            try
            {
                 var ass = Assembly.ReflectionOnlyLoadFrom( адресСборки );
                var types = ass.GetTypes();
                AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= CurrentDomain_ReflectionOnlyAssemblyResolve;
                return types;
            }
            catch (ReflectionTypeLoadException exception)
            {
                
                return exception.Types;
            }
            
           
        }

        static Assembly CurrentDomain_ReflectionOnlyAssemblyResolve ( object sender, ResolveEventArgs args )
        {
           return Assembly.ReflectionOnlyLoad(args.Name);
        }


        /// <summary>
        /// Команда: загрузи сборку в память
        /// </summary>
        /// <returns> </returns>
        public результат<Assembly> ЗагрузитьСборкуТипов ()
        {
            var рез = new результат<Assembly>();
            var имясборки = String.IsNullOrEmpty( ПолноеИмяСборки) ? new AssemblyName(): new AssemblyName( ПолноеИмяСборки );        

            имясборки.CodeBase = String.IsNullOrEmpty( АдресСборки) ? null:  АдресСборки;
            try
            {
                рез.что = Сборка = Assembly.Load( имясборки );

               // Скопировать Эту сборку в текущую папку
                // получить список требуемых сборок и других файлов из папки загружаемой сборки
                // скопировать файлы несовпадающие с файлами в текущей папке
                // могут быть сборки (файлы) с одинаковым именем, но разной датой создания, размером, номером сборки или ключом
                // создать подпапку для требуемых сборок
                // скопировать туда файлы
                // установить пути для поиска этих сборок

                //var setup = new AppDomainSetup();
                //var directoryInfo = (new FileInfo(АдресСборки)).Directory;
                //if (directoryInfo != null)
                //{
                //    var setup = AppDomain.CurrentDomain.SetupInformation;
                //    setup.PrivateBinPath = "cash";
                //}
                //    setup.ApplicationBase = directoryInfo.ToString() ;
                //setup.PrivateBinPath += ";   ";
                //setup.ShadowCopyFiles = "true"; // разрешить теневое копирование, файлы не блокируются в исходном месте
                //setup.DynamicBase = "";
                //// Shadow copying will not work unless the application has a name.
                //setup.ApplicationName = "MyApplication";
                //setup.CachePath = "путьКэша"; // реальный кэш = MyApplication\путьКэша файлы сами не удаляются

                ////Create evidence for the new application domain from evidence of
                //// current application domain.
                //Evidence adevidence = AppDomain.CurrentDomain.Evidence;

                //// Create a new application domain.
                //AppDomain domain = AppDomain.CreateDomain( "MyDomain", adevidence, setup );
                //// загружает сборку в домен как нейтральную к домену   если установлен Атрибут 
                //domain.Load(имясборки);          // используется в основном для СОМ взаимодействия
                ////Assembly.LoadFile()               загружает не нейтральную сборку Куда только?
                //domain.AppendPrivatePath( "Assemblies" );
                //AppDomain.CurrentDomain.AppendPrivatePath("подпапка");

                // лучше бы, если тип запрашивающий сборку смотрел место своей загрузки и загружал сборку оттуда   (НО !!!)
 
                // чтобы типы в сборке работали необходимы дополнительные сборки в этой же папке, которые framework не ищет
                // можно скопировать их текущую папку или если они подписаны в  GAC
                // а тем возможно еще нужны другие сборки - РЕКУРСИЯ 
            }
            catch ( Exception e )
            {
                рез.Успешно = false;
                рез.Исключения = e;
            }
            return рез;
        }
        /// <summary>
        /// Команда: найти тип по имени в текущем домене, вернуть ссылку на этот тип
        /// </summary>
        /// <param name="имя"> имя типа (краткое или полное(с пространством))  </param>
        /// <returns></returns>
        /// 
        public static Type НайтиТипВТекущемДомене ( string имя )
        {
            //return Type.GetType(имя);   имя полное квалифицированное имя типа = ПолноеимяТипа+ , + полное имя сборки
            // TopNamespace.SubNameSpace.ContainingClass+NestedClass, MyAssembly, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b17a5c561934e089
            var ass = СписокЗагруженныхСборок;
            var имяСодержитПространство = имя.Contains(".");
                                                                                                  
            foreach ( Assembly a in ass )
            {
                var ty = a.GetTypes();
                foreach ( Type t in ty )
                {
                    if ( t.Name == имя || t.FullName == имя ) { return t; }

                }
            }
            return null;
        }

        public Результат СоздатьНовыйОбъект ()
        {
            var рез = new Результат();
            try
            {
                var o =( Тип == typeof( string )) ? "" : Activator.CreateInstance( Тип ,true);
                Объект = o;

            }
            catch ( Exception e ) { рез.Успешно = false;
                рез.Исключения = e;
            }

            return рез;
        }

        public Результат ВосстановитьОбъект ()
        {
            var результат = new Результат();
            var рез = this.ЗагрузитьСборкуТипов();
            if ( !рез.Успешно ) return new Результат() { Успешно = false }; 

            if ( String.IsNullOrEmpty( ПолноеИмяТипа ) ) return результат;

            try
            {
                 Тип = рез.что.GetType( this.ПолноеИмяТипа );
            }
            catch (Exception)
            {
               return new Результат(){Успешно = false}; 
            }
           

            
            if ( this.СвойСохранительВосстановитель )// востановитель собственный
            {
                try
                {
                    Объект = this.Тип.InvokeMember( this.ИмяМетодаВосстановителя, BindingFlags.CreateInstance,
                                                                        null,
                                                                        new object[] { this.Файл }, null );
                }
                catch ( Exception e )
                {
                    результат.Исключения = e; результат.ТекстОшибки = e.Message;
                }
            }
            else // востановитель встроенный
            {
                try
                {
                    switch ( this.ИмяМетодаВосстановителя )
                    {// востановитель встроенный и не пустой
                        case "Восcтановить_из_xml":
                            this.Объект = ВосcтановитьИзXml( this.Файл, Тип );
                            break;
                        case "Восcтановить_из_SOAP":
                            this.Объект = ВосcтановитьИзSoap( this.Файл );
                            break;
                        case "Восcтановить_из_BinaryFormatter":
                            this.Объект = ВосcтановитьИзBinaryFormatter( this.Файл );
                            break;
                        default:// попытки востановления из всех форматов подряд
                            результат = ПопытатьсяВосcтановитьВнутреннимиВосстановителями();
                            break;
                    }

                }
                catch ( Exception e )
                {
                    результат.Успешно = false;
                    результат.ТекстОшибки = e.ToString();
                }
            }
            return результат;
        }

        private Результат ПопытатьсяВосcтановитьВнутреннимиВосстановителями ()
        {
            var результат = new Результат();

            try
            {
                this.Объект = ВосcтановитьИзXml( this.Файл,  Тип );
                this.ИмяМетодаВосстановителя = "Восcтановить_из_xml";
                this.ИмяМетодаСохранителя = "Сохранить_в_xml";
            }
            catch ( Exception e )
            {
                результат.ТекстОшибки = e.ToString() + "/n/r";
                try
                {
                    this.Объект = ВосcтановитьИзSoap( this.Файл );
                    this.ИмяМетодаВосстановителя = "Восcтановить_из_SOAP";
                    this.ИмяМетодаСохранителя = "Сохранить_в_SOAP";
                   
                }
                catch ( Exception e2 )
                {
                    результат.ТекстОшибки += e2.ToString() + "/n/r";
                    try
                    {
                        this.Объект = ВосcтановитьИзBinaryFormatter( this.Файл );
                        this.ИмяМетодаВосстановителя = "Восcтановить_из_BinaryFormatter";
                        this.ИмяМетодаСохранителя = "Сохранить_в_BinaryFormatter";
                    }
                    catch ( Exception e3 )
                    {
                        результат.ТекстОшибки += e3.ToString();
                        результат.Успешно = false;
                    }
                }
            }
            return результат;
        }

       

        /// <summary>
        /// Команда: сохрани состояние объекта на диске
        /// Если объект сохранен туда же сохраняется и паспорт
        /// </summary>
        /// <returns></returns>
        public Результат СохранитьСостояниеОбъекта ()
        {
            var рез = new Результат();
            if ( this.Объект == null ) { рез.Успешно = false; рез.Отчет = "объект отсутсвует"; Сохранитьпаспорт(); return рез; }
            if ( this.СвойСохранительВосстановитель )
            {
                try
                {
                    Тип.InvokeMember( ИмяМетодаСохранителя, BindingFlags.InvokeMethod, null, Объект, new[]{ Файл} );
                    Сохранитьпаспорт();
                    рез.Успешно = true; рез.Отчет = "Объект сохранен"; return рез;
                }
                catch ( Exception e )
                {
                    рез.Успешно = false; рез.Исключения = e; рез.Отчет = "свой сохранитель не работает, или не правильно задано имя файла";
                    Сохранитьпаспорт();
                    return рез;
                }

            }
            else
            {
                try
                {
                    switch ( this.ИмяМетодаСохранителя )
                    {
                        case "Сохранить_в_BinaryFormatter":
                            СохранитьВBinaryFormatter( this.Файл, this.Объект );
                            this.ИмяМетодаВосстановителя = "Восcтановить_из_BinaryFormatter";
                            break;
                        case "Сохранить_в_SOAP":
                            СохранитьВSoap( this.Файл, this.Объект );
                            this.ИмяМетодаВосстановителя = "Восcтановить_из_SOAP";
                            break;
                        case "Сохранить_в_xml":
                            СохранитьВXml( this.Файл, this.Объект );
                            this.ИмяМетодаВосстановителя = "Восcтановить_из_xml";
                            break;
                        default:
                            ПопытатьсяСохранитьВнутреннимиСохранителями();
                            break;
                    }
                }
                catch ( Exception e )
                {
                    рез.Успешно = false; рез.Исключения = e; Сохранитьпаспорт();
                    return рез;
                }
                Сохранитьпаспорт();

                return рез;
            }
        }

        /// <summary>
        ///   сохраняет паспорт в туже директорию что и сам объект
        /// </summary>
        public void Сохранитьпаспорт ()
        {
            var а = new FileInfo( this.Файл );
            if ( Directory.Exists( а.DirectoryName ) )
                if ( this.Имя != null | this.Имя != "" )
                    СохранитьВXml( this.Файл + ".паспорт", this );
        }
        public byte[] СохранитьПаспортВМассив()
        {
            var mem = new MemoryStream();
            var types = new Type[] { typeof (Guid)};
            var паспортXml = new XmlSerializer(typeof (Паспорт), types );
            паспортXml.Serialize( mem, this );
            var рез = mem.ToArray();
            mem.Dispose();
            return рез;
        }
        public static Паспорт ВосстановитьИзМассива( byte[] массив)
        {
            var mem = new MemoryStream(массив);
            var паспортXml = new XmlSerializer( typeof( Паспорт ) );
            var паспорт = ( Паспорт ) паспортXml.Deserialize( mem );
            mem.Dispose();
            return паспорт;
        }

        private void ПопытатьсяСохранитьВнутреннимиСохранителями ()
        {
            try
            {
                СохранитьВXml( this.Файл, this.Объект );
                this.ИмяМетодаВосстановителя = "Восcтановить_из_xml";
                this.ИмяМетодаСохранителя = "Сохранить_в_xml";
                return;
            }
            catch { }
            try
            {
                СохранитьВSoap( this.Файл, this.Объект );
                this.ИмяМетодаВосстановителя = "Восcтановить_из_SOAP";
                this.ИмяМетодаСохранителя = "Сохранить_в_SOAP";
                return;
            }
            catch { }
            try
            {
                СохранитьВBinaryFormatter( this.Файл, this.Объект );
                this.ИмяМетодаВосстановителя = "Восcтановить_из_BinaryFormatter";
                this.ИмяМетодаСохранителя = "Сохранить_в_BinaryFormatter";
                return;
            }
            catch ( Exception e )
            {
                throw e;
            }
        }

        public void УдалитьОбъектИзПамяти () // если другие объекты имеют ссылки на этот объект
        {                                                // то объект из памяти не будет удален
            this.Объект = null;                     // чтобы он исчез другие объекты должны иметьссылки
            //только на паспорт этого объекта
        }

        public void УдалитьОбъектСДиска ()
        {
            File.Delete( this.АдресСпячки );
        }

        public void ПереместитьМестоСпячкиОбъекта ( string новоеМестоСпячки )
        {
            File.Move( this.АдресСпячки, новоеМестоСпячки );
            this.АдресСпячки = новоеМестоСпячки;
        }


        // функции типа: сохранители восстановители встроенные
        public static void СохранитьВBinaryFormatter ( string файл, object о )
        {
            FileStream mystream = new FileStream( файл, FileMode.Create );
            try
            {
                BinaryFormatter formater = new BinaryFormatter();
                formater.Serialize( mystream, о );
            }
            catch ( Exception e )
            {
                mystream.Close();
                throw e;
            }
            mystream.Close();
        }

        public static object ВосcтановитьИзBinaryFormatter ( string файл )
        {
            FileStream mystream = new FileStream( файл, FileMode.Open, FileAccess.Read, FileShare.Read );
            BinaryFormatter formater = new BinaryFormatter();
            object o = formater.Deserialize( mystream );
            mystream.Close();
            return o;
        }


        public static void СохранитьВSoap ( string файл, object о )
        {
            FileStream mystream = new FileStream( файл, FileMode.Create );
            try
            {
                SoapFormatter formater = new SoapFormatter();
                formater.Serialize( mystream, о );
            }
            catch ( Exception e )
            {
                mystream.Close();
                throw e;
            }
            mystream.Close();
        }

        public static object ВосcтановитьИзSoap ( string файл )
        {
            FileStream mystream = new FileStream( файл, FileMode.Open, FileAccess.Read, FileShare.Read );
            SoapFormatter formater = new SoapFormatter();
            object o = formater.Deserialize( mystream );
            mystream.Close();
            return o;
        }


        public static void СохранитьВXml ( string файл, object o )
        {
            FileStream mystream = new FileStream( файл, FileMode.Create );
            try
            {
                XmlSerializer xml = new XmlSerializer( o.GetType() );
                xml.Serialize( mystream, o );
            }
            catch ( Exception e )
            {
                mystream.Close();
                throw e;
            }
            mystream.Close();

        }
        public static object ВосcтановитьИзXml ( string файл, Type T )
        {
            FileStream mystream = new FileStream( файл, FileMode.Open, FileAccess.Read, FileShare.Read );
                XmlSerializer xml = new XmlSerializer( T );
                object g = xml.Deserialize( mystream );
            mystream.Close();
            return g;
        }

   
        

       

    }
}
